home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / c / cdecl-2.000 / cdecl-2 / cdecl-2.5 / cdecl.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-16  |  31.7 KB  |  1,314 lines

  1. /*
  2.  * cdecl - ANSI C and C++ declaration composer & decoder
  3.  *
  4.  *    originally written
  5.  *        Graham Ross
  6.  *        once at tektronix!tekmdp!grahamr
  7.  *        now at Context, Inc.
  8.  *
  9.  *    modified to provide hints for unsupported types
  10.  *    added argument lists for functions
  11.  *    added 'explain cast' grammar
  12.  *    added #ifdef for 'create program' feature
  13.  *        ???? (sorry, I lost your name and login)
  14.  *
  15.  *    conversion to ANSI C
  16.  *        David Wolverton
  17.  *        ihnp4!houxs!daw
  18.  *
  19.  *    merged D. Wolverton's ANSI C version w/ ????'s version
  20.  *    added function prototypes
  21.  *    added C++ declarations
  22.  *    made type combination checking table driven
  23.  *    added checks for void variable combinations
  24.  *    made 'create program' feature a runtime option
  25.  *    added file parsing as well as just stdin
  26.  *    added help message at beginning
  27.  *    added prompts when on a TTY or in interactive mode
  28.  *    added getopt() usage
  29.  *    added -a, -r, -p, -c, -d, -D, -V, -i and -+ options
  30.  *    delinted
  31.  *    added #defines for those without getopt or void
  32.  *    added 'set options' command
  33.  *    added 'quit/exit' command
  34.  *    added synonyms
  35.  *        Tony Hansen
  36.  *        attmail!tony, ihnp4!pegasus!hansen
  37.  *
  38.  *    added extern, register, static
  39.  *    added links to explain, cast, declare
  40.  *    separately developed ANSI C support
  41.  *        Merlyn LeRoy
  42.  *        merlyn@rose3.rosemount.com
  43.  *
  44.  *    merged versions from LeRoy
  45.  *    added tmpfile() support
  46.  *    allow more parts to be missing during explanations
  47.  *        Tony Hansen
  48.  *        attmail!tony, ihnp4!pegasus!hansen
  49.  *
  50.  *    added GNU readline() support
  51.  *    added dotmpfile_from_string() to support readline()
  52.  *    outdented C help text to prevent line from wrapping
  53.  *    minor tweaking of makefile && mv makefile Makefile
  54.  *    took out interactive and nointeractive commands when
  55.  *        compiled with readline support
  56.  *    added prompt and noprompt commands, -q option
  57.  *        Dave Conrad
  58.  *        conrad@detroit.freenet.org
  59.  */
  60.  
  61. char cdeclsccsid[] = "@(#)cdecl.c    2.5 1/15/96";
  62.  
  63. #include <stdio.h>
  64. #include <ctype.h>
  65. #if __STDC__ || defined(DOS)
  66. # include <stdlib.h>
  67. # include <stddef.h>
  68. # include <string.h>
  69. # include <stdarg.h>
  70. #else
  71. # ifndef NOVARARGS
  72. #  include <varargs.h>
  73. # endif /* ndef NOVARARGS */
  74. char *malloc();
  75. void free(), exit(), perror();
  76. # ifdef BSD
  77. #  include <strings.h>
  78.    extern int errno;
  79. #  define strrchr rindex
  80. #  define NOTMPFILE
  81. # else
  82. #  include <string.h>
  83. #  include <errno.h>
  84. # endif /* BSD */
  85. # ifdef NOVOID
  86. #  define void int
  87. # endif /* NOVOID */
  88. #endif /* __STDC__ || DOS */
  89.  
  90. #ifdef USE_READLINE
  91. # include <readline/readline.h>
  92.   /* prototypes for functions related to readline() */
  93.   char * getline();
  94.   char ** attempt_completion(char *, int, int);
  95.   char * keyword_completion(char *, int);
  96.   char * command_completion(char *, int);
  97. #endif
  98.  
  99. /* maximum # of chars from progname to display in prompt */
  100. #define MAX_NAME 32
  101.  
  102. /* this is the prompt for readline() to display */
  103. char cdecl_prompt[MAX_NAME+3];
  104.  
  105. /* backup copy of prompt to save it while prompting is off */
  106. char real_prompt[MAX_NAME+3];
  107.  
  108. #define    MB_SHORT    0001
  109. #define    MB_LONG        0002
  110. #define    MB_UNSIGNED    0004
  111. #define MB_INT        0010
  112. #define MB_CHAR        0020
  113. #define MB_FLOAT    0040
  114. #define MB_DOUBLE    0100
  115. #define MB_VOID        0200
  116. #define    MB_SIGNED    0400
  117.  
  118. #define NullCP ((char*)NULL)
  119. #ifdef dodebug
  120. # define Debug(x) do { if (DebugFlag) (void) fprintf x; } while (0)
  121. #else
  122. # define Debug(x) /* nothing */
  123. #endif
  124.  
  125. #if __STDC__
  126.   char *ds(char *), *cat(char *, ...), *visible(int);
  127.   int getopt(int,char **,char *);
  128.   int main(int, char **);
  129.   int yywrap(void);
  130.   int dostdin(void);
  131.   void mbcheck(void), dohelp(void), usage(void);
  132.   void prompt(void), doprompt(void), noprompt(void);
  133.   void unsupp(char *, char *);
  134.   void notsupported(char *, char *, char *);
  135.   void yyerror(char *);
  136.   void doset(char *);
  137.   void dodeclare(char*, char*, char*, char*, char*);
  138.   void docast(char*, char*, char*, char*);
  139.   void dodexplain(char*, char*, char*, char*);
  140.   void docexplain(char*, char*, char*, char*);
  141.   void setprogname(char *);
  142.   int dotmpfile(int, char**), dofileargs(int, char**);
  143. #else
  144.   char *ds(), *cat(), *visible();
  145.   int getopt();
  146.   void mbcheck(), dohelp(), usage();
  147.   void prompt(), doprompt(), noprompt();
  148.   void unsupp(), notsupported();
  149.   void yyerror();
  150.   void doset(), dodeclare(), docast(), dodexplain(), docexplain();
  151.   void setprogname();
  152.   int dotmpfile(), dofileargs();
  153. #endif /* __STDC__ */
  154.   FILE *tmpfile();
  155.  
  156. /* variables used during parsing */
  157. unsigned modbits = 0;
  158. int arbdims = 1;
  159. char *savedname = 0;
  160. char unknown_name[] = "unknown_name";
  161. char prev = 0;        /* the current type of the variable being examined */
  162.             /*    values    type                   */
  163.             /*    p    pointer                   */
  164.             /*    r    reference               */
  165.             /*    f    function               */
  166.             /*    a    array (of arbitrary dimensions)    */
  167.             /*    A    array with dimensions           */
  168.             /*    n    name                   */
  169.             /*    v    void                   */
  170.             /*    s    struct | class               */
  171.             /*    t    simple type (int, long, etc.)       */
  172.  
  173. /* options */
  174. int RitchieFlag = 0;        /* -r, assume Ritchie PDP C language */
  175. int MkProgramFlag = 0;        /* -c, output {} and ; after declarations */
  176. int PreANSIFlag = 0;        /* -p, assume pre-ANSI C language */
  177. int CplusplusFlag = 0;        /* -+, assume C++ language */
  178. int OnATty = 0;            /* stdin is coming from a terminal */
  179. int Interactive = 0;        /* -i, overrides OnATty */
  180. int KeywordName = 0;        /* $0 is a keyword (declare, explain, cast) */
  181. char *progname = "cdecl";    /* $0 */
  182. int quiet = 0;                  /* -q, quiets prompt and initial help msg */
  183.  
  184. #if dodebug
  185. int DebugFlag = 0;        /* -d, output debugging trace info */
  186. #endif
  187.  
  188. #ifdef doyydebug        /* compile in yacc trace statements */
  189. #define YYDEBUG 1
  190. #endif /* doyydebug */
  191.  
  192. #include "cdgram.c"
  193. #include "cdlex.c"
  194.  
  195. /* definitions (and abbreviations) for type combinations cross check table */
  196. #define ALWAYS    0    /* combo always okay */
  197. #define _    ALWAYS
  198. #define NEVER    1    /* combo never allowed */
  199. #define X    NEVER
  200. #define RITCHIE    2    /* combo not allowed in Ritchie compiler */
  201. #define R    RITCHIE
  202. #define PREANSI    3    /* combo not allowed in Pre-ANSI compiler */
  203. #define P    PREANSI
  204. #define ANSI    4    /* combo not allowed anymore in ANSI compiler */
  205. #define A    ANSI
  206.  
  207. /* This is an lower left triangular array. If we needed */
  208. /* to save 9 bytes, the "long" row can be removed. */
  209. char crosscheck[9][9] = {
  210.     /*            L, I, S, C, V, U, S, F, D, */
  211.     /* long */        _, _, _, _, _, _, _, _, _,
  212.     /* int */        _, _, _, _, _, _, _, _, _,
  213.     /* short */        X, _, _, _, _, _, _, _, _,
  214.     /* char */        X, X, X, _, _, _, _, _, _,
  215.     /* void */        X, X, X, X, _, _, _, _, _,
  216.     /* unsigned */    R, _, R, R, X, _, _, _, _,
  217.     /* signed */    P, P, P, P, X, X, _, _, _,
  218.     /* float */        A, X, X, X, X, X, X, _, _,
  219.     /* double */    P, X, X, X, X, X, X, X, _
  220. };
  221.  
  222. /* the names and bits checked for each row in the above array */
  223. struct
  224.     {
  225.     char *name;
  226.     int bit;
  227.     } crosstypes[9] =
  228.     {
  229.         { "long",        MB_LONG        },
  230.         { "int",        MB_INT        },
  231.         { "short",        MB_SHORT    },
  232.         { "char",        MB_CHAR        },
  233.         { "void",        MB_VOID        },
  234.         { "unsigned",    MB_UNSIGNED    },
  235.         { "signed",        MB_SIGNED    },
  236.         { "float",        MB_FLOAT    },
  237.         { "double",        MB_DOUBLE    }
  238.     };
  239.  
  240. /* Run through the crosscheck array looking */
  241. /* for unsupported combinations of types. */
  242. void mbcheck()
  243. {
  244.     register int i, j, restrict;
  245.     char *t1, *t2;
  246.  
  247.     /* Loop through the types */
  248.     /* (skip the "long" row) */
  249.     for (i = 1; i < 9; i++)
  250.     {
  251.     /* if this type is in use */
  252.     if ((modbits & crosstypes[i].bit) != 0)
  253.         {
  254.         /* check for other types also in use */
  255.         for (j = 0; j < i; j++)
  256.         {
  257.         /* this type is not in use */
  258.         if (!(modbits & crosstypes[j].bit))
  259.             continue;
  260.         /* check the type of restriction */
  261.         restrict = crosscheck[i][j];
  262.         if (restrict == ALWAYS)
  263.             continue;
  264.         t1 = crosstypes[i].name;
  265.         t2 = crosstypes[j].name;
  266.         if (restrict == NEVER)
  267.             {
  268.             notsupported("", t1, t2);
  269.             }
  270.         else if (restrict == RITCHIE)
  271.             {
  272.             if (RitchieFlag)
  273.             notsupported(" (Ritchie Compiler)", t1, t2);
  274.             }
  275.         else if (restrict == PREANSI)
  276.             {
  277.             if (PreANSIFlag || RitchieFlag)
  278.             notsupported(" (Pre-ANSI Compiler)", t1, t2);
  279.             }
  280.         else if (restrict == ANSI)
  281.             {
  282.             if (!RitchieFlag && !PreANSIFlag)
  283.             notsupported(" (ANSI Compiler)", t1, t2);
  284.             }
  285.         else
  286.             {
  287.             (void) fprintf (stderr,
  288.             "%s: Internal error in crosscheck[%d,%d]=%d!\n",
  289.             progname, i, j, restrict);
  290.             exit(1); /* NOTREACHED */
  291.             }
  292.         }
  293.         }
  294.     }
  295. }
  296.  
  297. /* undefine these as they are no longer needed */
  298. #undef _
  299. #undef ALWAYS
  300. #undef X
  301. #undef NEVER
  302. #undef R
  303. #undef RITCHIE
  304. #undef P
  305. #undef PREANSI
  306. #undef A
  307. #undef ANSI
  308.  
  309. #ifdef USE_READLINE
  310.  
  311. /* this section contains functions and declarations used with readline() */
  312.  
  313. /* the readline info pages make this clearer than any comments possibly
  314.  * could, so see them for more information
  315.  */
  316.  
  317. char *commands[] = {
  318.   "declare",
  319.   "explain",
  320.   "cast",
  321.   "help",
  322.   "set",
  323.   "exit",
  324.   "quit",
  325.   NULL
  326. };
  327.  
  328. char *keywords[] = {
  329.   "function",
  330.   "returning",
  331.   "array",
  332.   "pointer",
  333.   "reference",
  334.   "member",
  335.   "const",
  336.   "volatile",
  337.   "noalias",
  338.   "struct",
  339.   "union",
  340.   "enum",
  341.   "class",
  342.   "extern",
  343.   "static",
  344.   "auto",
  345.   "register",
  346.   "short",
  347.   "long",
  348.   "signed",
  349.   "unsigned",
  350.   "char",
  351.   "float",
  352.   "double",
  353.   "void",
  354.   NULL
  355. };
  356.  
  357. char *options[] = {
  358.   "options",
  359.   "create",
  360.   "nocreate",
  361.   "prompt",
  362.   "noprompt",
  363. #if 0
  364.   "interactive",
  365.   "nointeractive",
  366. #endif
  367.   "ritchie",
  368.   "preansi",
  369.   "ansi",
  370.   "cplusplus",
  371.   NULL
  372. };
  373.  
  374. /* A static variable for holding the line. */
  375. static char *line_read = NULL;
  376.  
  377. /* Read a string, and return a pointer to it.  Returns NULL on EOF. */
  378. char * getline ()
  379. {
  380.   /* If the buffer has already been allocated, return the memory
  381.      to the free pool. */
  382.   if (line_read != NULL)
  383.     {
  384.       free (line_read);
  385.       line_read = NULL;
  386.     }
  387.  
  388.   /* Get a line from the user. */
  389.   line_read = readline (cdecl_prompt);
  390.  
  391.   /* If the line has any text in it, save it on the history. */
  392.   if (line_read && *line_read)
  393.     add_history (line_read);
  394.  
  395.   return (line_read);
  396. }
  397.  
  398. char ** attempt_completion(char *text, int start, int end)
  399. {
  400.   char **matches = NULL;
  401.  
  402.   if (start == 0) matches = completion_matches(text, command_completion);
  403.  
  404.   return matches;
  405. }
  406.  
  407. char * command_completion(char *text, int flag)
  408. {
  409.   static int index, len;
  410.   char *command;
  411.  
  412.   if (!flag) {
  413.     index = 0;
  414.     len = strlen(text);
  415.   }
  416.  
  417.   while (command = commands[index]) {
  418.     index++;
  419.     if (!strncmp(command, text, len)) return strdup(command);
  420.   }
  421.   return NULL;
  422. }
  423.  
  424. char * keyword_completion(char *text, int flag)
  425. {
  426.   static int index, len, set, into;
  427.   char *keyword, *option;
  428.  
  429.   if (!flag) {
  430.     index = 0;
  431.     len = strlen(text);
  432.     /* completion works differently if the line begins with "set" */
  433.     set = !strncmp(rl_line_buffer, "set", 3);
  434.     into = 0;
  435.   }
  436.  
  437.   if (set) {
  438.     while (option = options[index]) {
  439.       index++;
  440.       if (!strncmp(option, text, len)) return strdup(option);
  441.     }
  442.   } else {
  443.     /* handle "int" and "into" as special cases */
  444.     if (!into) {
  445.       into = 1;
  446.       if (!strncmp(text, "into", len) && strncmp(text, "int", len))
  447.         return strdup("into");
  448.       if (strncmp(text, "int", len)) return keyword_completion(text, into);
  449.       /* normally "int" and "into" would conflict with one another when
  450.        * completing; cdecl tries to guess which one you wanted, and it
  451.        * always guesses correctly
  452.        */
  453.       if (!strncmp(rl_line_buffer, "cast", 4)
  454.           && !strstr(rl_line_buffer, "into"))
  455.         return strdup("into");
  456.       else
  457.         return strdup("int");
  458.     } else while (keyword = keywords[index]) {
  459.       index++;
  460.       if (!strncmp(keyword, text, len)) return strdup(keyword);
  461.     }
  462.   }
  463.   return NULL;
  464. }
  465. #endif /* USE_READLINE */
  466.  
  467. /* Write out a message about something */
  468. /* being unsupported, possibly with a hint. */
  469. void unsupp(s,hint)
  470. char *s,*hint;
  471. {
  472.     notsupported("", s, NullCP);
  473.     if (hint)
  474.     (void) fprintf(stderr, "\t(maybe you mean \"%s\")\n", hint);
  475. }
  476.  
  477. /* Write out a message about something */
  478. /* being unsupported on a particular compiler. */
  479. void notsupported(compiler, type1, type2)
  480. char *compiler, *type1, *type2;
  481. {
  482.     if (type2)
  483.     (void) fprintf(stderr,
  484.         "Warning: Unsupported in%s C%s -- '%s' with '%s'\n",
  485.         compiler, CplusplusFlag ? "++" : "", type1, type2);
  486.     else
  487.     (void) fprintf(stderr,
  488.         "Warning: Unsupported in%s C%s -- '%s'\n",
  489.         compiler, CplusplusFlag ? "++" : "", type1);
  490. }
  491.  
  492. /* Called by the yacc grammar */
  493. void yyerror(s)
  494. char *s;
  495. {
  496.     (void) printf("%s\n",s);
  497.     Debug((stdout, "yychar=%d\n", yychar));
  498. }
  499.  
  500. /* Called by the yacc grammar */
  501. int yywrap()
  502. {
  503.     return 1;
  504. }
  505.  
  506. /*
  507.  * Support for dynamic strings:
  508.  * cat() creates a string from the concatenation
  509.  * of a null terminated list of input strings.
  510.  * The input strings are free()'d by cat()
  511.  * (so they better have been malloc()'d).
  512.  *
  513.  * the different methods of <stdarg.h> and
  514.  * <vararg.h> are handled within these macros
  515.  */
  516. #if __STDC__
  517. #  define VA_DCL(type,var)        (type var,...)
  518. #  define VA_START(list,var,type)    ((va_start(list,var)) , (var))
  519. #else
  520. #if defined(DOS)
  521. #  define VA_DCL(type,var)        (var,...) type var;
  522. #  define VA_START(list,var,type)    ((va_start(list,var)) , (var))
  523. #else
  524. #ifndef NOVARARGS
  525. # define VA_DCL(type,var)        (va_alist) va_dcl
  526. # define VA_START(list,var,type)    ((va_start(list)) , va_arg(list,type))
  527. #else
  528.    /*
  529.     *    it is assumed here that machines which don't have either
  530.     *    <varargs.h> or <stdarg.h> will put its arguments on
  531.     *    the stack in the "usual" way and consequently can grab
  532.     *    the arguments using the "take the address of the first
  533.     *    parameter and increment by sizeof" trick.
  534.     */
  535. # define VA_DCL(type,var)        (var) type var;
  536. # define VA_START(list,var,type)    (list = (va_list)&(var) , (var))
  537. # define va_arg(list,type)        ((type *)(list += sizeof(type)))[-1]
  538. # define va_end(p)            /* nothing */
  539. typedef char *va_list;
  540. #endif /* NOVARARGS */
  541. #endif /* DOS */
  542. #endif /* __STDC__ */
  543.  
  544. /* VARARGS */
  545. char *cat
  546. VA_DCL(char*, s1)
  547. {
  548.     register char *newstr;
  549.     register unsigned len = 1;
  550.     char *str;
  551.     va_list args;
  552.  
  553.     /* find the length which needs to be allocated */
  554.     str = VA_START(args, s1, char*);
  555.     for ( ; str; str = va_arg(args, char*))
  556.     len += strlen(str);
  557.     va_end(args);
  558.  
  559.     /* allocate it */
  560.     newstr = malloc(len);
  561.     if (newstr == 0)
  562.     {
  563.     (void) fprintf (stderr, "%s: out of malloc space within cat()!\n",
  564.         progname);
  565.     exit(1);
  566.     }
  567.     newstr[0] = '\0';
  568.  
  569.     /* copy in the strings */
  570.     str = VA_START(args, s1, char*);
  571.     for ( ; str; str = va_arg(args, char*))
  572.     {
  573.     (void) strcat(newstr,str);
  574.     free(str);
  575.     }
  576.     va_end(args);
  577.  
  578.     Debug((stderr, "\tcat created '%s'\n", newstr));
  579.     return newstr;
  580. }
  581.  
  582. /*
  583.  * ds() makes a malloc()'d string from one that's not.
  584.  */
  585. char *ds(s)
  586. char *s;
  587. {
  588.     register char *p = malloc((unsigned)(strlen(s)+1));
  589.  
  590.     if (p)
  591.     (void) strcpy(p,s);
  592.     else
  593.     {
  594.     (void) fprintf (stderr, "%s: malloc() failed!\n", progname);
  595.     exit(1);
  596.     }
  597.     return p;
  598. }
  599.  
  600. /* return a visible representation of a character */
  601. char *visible(c)
  602. int c;
  603. {
  604.     static char buf[5];
  605.  
  606.     c &= 0377;
  607.     if (isprint(c))
  608.     {
  609.     buf[0] = c;
  610.     buf[1] = '\0';
  611.     }
  612.     else
  613.     (void) sprintf(buf,"\\%03o",c);
  614.     return buf;
  615. }
  616.  
  617. #ifdef NOTMPFILE
  618. /* provide a conservative version of tmpfile() */
  619. /* for those systems without it. */
  620. /* tmpfile() returns a FILE* of a file opened */
  621. /* for read&write. It is supposed to be */
  622. /* automatically removed when it gets closed, */
  623. /* but here we provide a separate rmtmpfile() */
  624. /* function to perform that function. */
  625. /* Also provide several possible file names to */
  626. /* try for opening. */
  627. static char *file4tmpfile = 0;
  628.  
  629. FILE *tmpfile()
  630. {
  631.     static char *listtmpfiles[] =
  632.     {
  633.     "/usr/tmp/cdeclXXXXXX",
  634.     "/tmp/cdeclXXXXXX",
  635.     "/cdeclXXXXXX",
  636.     "cdeclXXXXXX",
  637.     0
  638.     };
  639.  
  640.     char **listp = listtmpfiles;
  641.     for ( ; *listp; listp++)
  642.     {
  643.     FILE *retfp;
  644.     (void) mktemp(*listp);
  645.     retfp = fopen(*listp, "w+");
  646.     if (!retfp)
  647.         continue;
  648.     file4tmpfile = *listp;
  649.     return retfp;
  650.     }
  651.  
  652.     return 0;
  653. }
  654.  
  655. void rmtmpfile()
  656. {
  657.     if (file4tmpfile)
  658.     (void) unlink(file4tmpfile);
  659. }
  660. #else
  661. /* provide a mock rmtmpfile() for normal systems */
  662. # define rmtmpfile()    /* nothing */
  663. #endif /* NOTMPFILE */
  664.  
  665. #ifndef NOGETOPT
  666. extern int optind;
  667. #else
  668. /* This is a miniature version of getopt() which will */
  669. /* do just barely enough for us to get by below. */
  670. /* Options are not allowed to be bunched up together. */
  671. /* Option arguments are not supported. */
  672. int optind = 1;
  673.  
  674. int getopt(argc,argv,optstring)
  675. char **argv;
  676. char *optstring;
  677. {
  678.     int ret;
  679.     char *p;
  680.  
  681.     if ((argv[optind][0] != '-')
  682. #ifdef DOS
  683.     && (argv[optind][0] != '/')
  684. #endif /* DOS */
  685.     )
  686.     return EOF;
  687.  
  688.     ret = argv[optind][1];
  689.     optind++;
  690.  
  691.     for (p = optstring; *p; p++)
  692.     if (*p == ret)
  693.         return ret;
  694.  
  695.     (void) fprintf (stderr, "%s: illegal option -- %s\n",
  696.     progname, visible(ret));
  697.  
  698.     return '?';
  699. }
  700. #endif
  701.  
  702. /* the help messages */
  703. struct helpstruct
  704.     {
  705.     char *text;    /* generic text */
  706.     char *cpptext;    /* C++ specific text */
  707.     } helptext[] =
  708.     {    /* up-to 23 lines of help text so it fits on (24x80) screens */
  709. /*  1 */{ "[] means optional; {} means 1 or more; <> means defined elsewhere", 0 },
  710. /*  2 */{ "  commands are separated by ';' and newlines", 0 },
  711. /*  3 */{ "command:", 0 },
  712. /*  4 */{ "  declare <name> as <english>", 0 },
  713. /*  5 */{ "  cast <name> into <english>", 0 },
  714. /*  6 */{ "  explain <gibberish>", 0 },
  715. /*  7 */{ "  set or set options", 0 },
  716. /*  8 */{ "  help, ?", 0 },
  717. /*  9 */{ "  quit or exit", 0 },
  718. /* 10 */{ "english:", 0 },
  719. /* 11 */{ "  function [( <decl-list> )] returning <english>", 0 },
  720. /* 12 */{ "  array [<number>] of <english>", 0 },
  721. /* 13 */{ "  [{ const | volatile | noalias }] pointer to <english>",
  722.       "  [{const|volatile}] {pointer|reference} to [member of class <name>] <english>" },
  723. /* 14 */{ "  <type>", 0 },
  724. /* 15 */{ "type:", 0 },
  725. /* 16 */{ "  {[<storage-class>] [{<modifier>}] [<C-type>]}", 0 },
  726. /* 17 */{ "  { struct | union | enum } <name>",
  727.       "  {struct|class|union|enum} <name>" },
  728. /* 18 */{ "decllist: a comma separated list of <name>, <english> or <name> as <english>", 0 },
  729. /* 19 */{ "name: a C identifier", 0 },
  730. /* 20 */{ "gibberish: a C declaration, like 'int *x', or cast, like '(int *)x'", 0 },
  731. /* 21 */{ "storage-class: extern, static, auto, register", 0 },
  732. /* 22 */{ "C-type: int, char, float, double, or void", 0 },
  733. /* 23 */{ "modifier: short, long, signed, unsigned, const, volatile, or noalias",
  734.       "modifier: short, long, signed, unsigned, const, or volatile" },
  735.     { 0, 0 }
  736.     };
  737.  
  738. /* Print out the help text */
  739. void dohelp()
  740. {
  741.     register struct helpstruct *p;
  742.     register char *fmt = CplusplusFlag ? " %s\n" : "  %s\n";
  743.  
  744.     for (p = helptext; p->text; p++)
  745.     if (CplusplusFlag && p->cpptext)
  746.         (void) printf(fmt, p->cpptext);
  747.     else
  748.         (void) printf(fmt, p->text);
  749. }
  750.  
  751. /* Tell how to invoke cdecl. */
  752. void usage()
  753. {
  754.     (void) fprintf (stderr, "Usage: %s [-r|-p|-a|-+] [-ciq%s%s] [files...]\n",
  755.     progname,
  756. #ifdef dodebug
  757.     "d",
  758. #else
  759.     "",
  760. #endif /* dodebug */
  761. #ifdef doyydebug
  762.     "D"
  763. #else
  764.     ""
  765. #endif /* doyydebug */
  766.     );
  767.     (void) fprintf (stderr, "\t-r Check against Ritchie PDP C Compiler\n");
  768.     (void) fprintf (stderr, "\t-p Check against Pre-ANSI C Compiler\n");
  769.     (void) fprintf (stderr, "\t-a Check against ANSI C Compiler%s\n",
  770.     CplusplusFlag ? "" : " (the default)");
  771.     (void) fprintf (stderr, "\t-+ Check against C++ Compiler%s\n",
  772.     CplusplusFlag ? " (the default)" : "");
  773.     (void) fprintf (stderr, "\t-c Create compilable output (include ; and {})\n");
  774.     (void) fprintf (stderr, "\t-i Force interactive mode\n");
  775.     (void) fprintf (stderr, "\t-q Quiet prompt\n");
  776. #ifdef dodebug
  777.     (void) fprintf (stderr, "\t-d Turn on debugging mode\n");
  778. #endif /* dodebug */
  779. #ifdef doyydebug
  780.     (void) fprintf (stderr, "\t-D Turn on YACC debugging mode\n");
  781. #endif /* doyydebug */
  782.     exit(1);
  783.     /* NOTREACHED */
  784. }
  785.  
  786. /* Manage the prompts. */
  787. static int prompting;
  788.  
  789. void doprompt() { prompting = 1; }
  790. void noprompt() { prompting = 0; }
  791.  
  792. void prompt()
  793. {
  794. #ifndef USE_READLINE
  795.     if ((OnATty || Interactive) && prompting) {
  796.     (void) printf("%s", cdecl_prompt);
  797. # if 0
  798.     (void) printf("%s> ", progname);
  799. # endif /* that was the old way to display the prompt */
  800.     (void) fflush(stdout);
  801.     }
  802. #endif
  803. }
  804.  
  805. /* Save away the name of the program from argv[0] */
  806. void setprogname(argv0)
  807. char *argv0;
  808. {
  809. #ifdef DOS
  810.     char *dot;
  811. #endif /* DOS */
  812.  
  813.     progname = strrchr(argv0, '/');
  814.  
  815. #ifdef DOS
  816.     if (!progname)
  817.     progname = strrchr(argv0, '\\');
  818. #endif /* DOS */
  819.  
  820.     if (progname)
  821.     progname++;
  822.     else
  823.     progname = argv0;
  824.  
  825. #ifdef DOS
  826.     dot = strchr(progname, '.');
  827.     if (dot)
  828.     *dot = '\0';
  829.     for (dot = progname; *dot; dot++)
  830.     *dot = tolower(*dot);
  831. #endif /* DOS */
  832.     /* this sets up the prompt, which is on by default */
  833.     {
  834.     int len;
  835.  
  836.     len = strlen(progname);
  837.     if (len > MAX_NAME) len = MAX_NAME;
  838.     strncpy(real_prompt, progname, len);
  839.     real_prompt[len] = '>';
  840.     real_prompt[len+1] = ' ';
  841.     real_prompt[len+2] = '\0';
  842.     }
  843. }
  844.  
  845. /* Run down the list of keywords to see if the */
  846. /* program is being called named as one of them */
  847. /* or the first argument is one of them. */
  848. int namedkeyword(argn)
  849. char *argn;
  850. {
  851.     static char *cmdlist[] =
  852.     {
  853.     "explain", "declare", "cast", "help", "?", "set", 0
  854.     };
  855.  
  856.     /* first check the program name */
  857.     char **cmdptr = cmdlist;
  858.     for ( ; *cmdptr; cmdptr++)
  859.     if (strcmp(*cmdptr, progname) == 0)
  860.         {
  861.         KeywordName = 1;
  862.         return 1;
  863.         }
  864.  
  865.     /* now check $1 */
  866.     for (cmdptr = cmdlist; *cmdptr; cmdptr++)
  867.     if (strcmp(*cmdptr, argn) == 0)
  868.         return 1;
  869.  
  870.     /* nope, must be file name arguments */
  871.     return 0;
  872. }
  873.  
  874. /* Read from standard input, turning */
  875. /* on prompting if necessary. */
  876. int dostdin()
  877. {
  878.     int ret;
  879.     if (OnATty || Interactive)
  880.     {
  881. #ifndef USE_READLINE
  882.     if (!quiet) (void) printf("Type `help' or `?' for help\n");
  883.     prompt();
  884. #else
  885.     char *line, *oldline;
  886.     int len, newline;
  887.  
  888.     if (!quiet) (void) printf("Type `help' or `?' for help\n");
  889.     ret = 0;
  890.     while ((line = getline())) {
  891.         if (!strcmp(line, "quit") || !strcmp(line, "exit")) {
  892.         free(line);
  893.         return ret;
  894.         }
  895.         newline = 0;
  896.         /* readline() strips newline, we add semicolon if necessary */
  897.         len = strlen(line);
  898.         if (len && line[len-1] != '\n' && line[len-1] != ';') {
  899.         newline = 1;
  900.         oldline = line;
  901.         line = malloc(len+2);
  902.         strcpy(line, oldline);
  903.         line[len] = ';';
  904.         line[len+1] = '\0';
  905.         }
  906.         if (len) ret = dotmpfile_from_string(line);
  907.         if (newline) free(line);
  908.     }
  909.     puts("");
  910.     return ret;
  911. #endif
  912.     }
  913.  
  914.     yyin = stdin;
  915.     ret = yyparse();
  916.     OnATty = 0;
  917.     return ret;
  918. }
  919.  
  920. #ifdef USE_READLINE
  921. /* Write a string into a file and treat that file as the input. */
  922. int dotmpfile_from_string(s)
  923. char *s;
  924. {
  925.     int ret = 0;
  926.     FILE *tmpfp = tmpfile();
  927.     if (!tmpfp)
  928.     {
  929.     int sverrno = errno;
  930.     (void) fprintf (stderr, "%s: cannot open temp file\n",
  931.         progname);
  932.     errno = sverrno;
  933.     perror(progname);
  934.     return 1;
  935.     }
  936.  
  937.     if (fputs(s, tmpfp) == EOF)
  938.     {
  939.     int sverrno;
  940.     sverrno = errno;
  941.     (void) fprintf (stderr, "%s: error writing to temp file\n",
  942.         progname);
  943.     errno = sverrno;
  944.     perror(progname);
  945.     (void) fclose(tmpfp);
  946.     rmtmpfile();
  947.     return 1;
  948.     }
  949.  
  950.     rewind(tmpfp);
  951.     yyin = tmpfp;
  952.     ret += yyparse();
  953.     (void) fclose(tmpfp);
  954.     rmtmpfile();
  955.  
  956.     return ret;
  957. }
  958. #endif /* USE_READLINE */
  959.  
  960. /* Write the arguments into a file */
  961. /* and treat that file as the input. */
  962. int dotmpfile(argc, argv)
  963. int argc;
  964. char **argv;
  965. {
  966.     int ret = 0;
  967.     FILE *tmpfp = tmpfile();
  968.     if (!tmpfp)
  969.     {
  970.     int sverrno = errno;
  971.     (void) fprintf (stderr, "%s: cannot open temp file\n",
  972.         progname);
  973.     errno = sverrno;
  974.     perror(progname);
  975.     return 1;
  976.     }
  977.  
  978.     if (KeywordName)
  979.     if (fputs(progname, tmpfp) == EOF)
  980.         {
  981.         int sverrno;
  982.     errwrite:
  983.         sverrno = errno;
  984.         (void) fprintf (stderr, "%s: error writing to temp file\n",
  985.         progname);
  986.         errno = sverrno;
  987.         perror(progname);
  988.         (void) fclose(tmpfp);
  989.         rmtmpfile();
  990.         return 1;
  991.         }
  992.  
  993.     for ( ; optind < argc; optind++)
  994.     if (fprintf(tmpfp, " %s", argv[optind]) == EOF)
  995.         goto errwrite;
  996.  
  997.     if (putc('\n', tmpfp) == EOF)
  998.     goto errwrite;
  999.  
  1000.     rewind(tmpfp);
  1001.     yyin = tmpfp;
  1002.     ret += yyparse();
  1003.     (void) fclose(tmpfp);
  1004.     rmtmpfile();
  1005.  
  1006.     return ret;
  1007. }
  1008.  
  1009. /* Read each of the named files for input. */
  1010. int dofileargs(argc, argv)
  1011. int argc;
  1012. char **argv;
  1013. {
  1014.     FILE *ifp;
  1015.     int ret = 0;
  1016.  
  1017.     for ( ; optind < argc; optind++)
  1018.     if (strcmp(argv[optind], "-") == 0)
  1019.         ret += dostdin();
  1020.  
  1021.     else if ((ifp = fopen(argv[optind], "r")) == NULL)
  1022.         {
  1023.         int sverrno = errno;
  1024.         (void) fprintf (stderr, "%s: cannot open %s\n",
  1025.         progname, argv[optind]);
  1026.         errno = sverrno;
  1027.         perror(argv[optind]);
  1028.         ret++;
  1029.         }
  1030.  
  1031.     else
  1032.         {
  1033.         yyin = ifp;
  1034.         ret += yyparse();
  1035.         }
  1036.  
  1037.     return ret;
  1038. }
  1039.  
  1040. /* print out a cast */
  1041. void docast(name, left, right, type)
  1042. char *name, *left, *right, *type;
  1043. {
  1044.     int lenl = strlen(left), lenr = strlen(right);
  1045.  
  1046.     if (prev == 'f')
  1047.         unsupp("Cast into function",
  1048.             "cast into pointer to function");
  1049.     else if (prev=='A' || prev=='a')
  1050.         unsupp("Cast into array","cast into pointer");
  1051.     (void) printf("(%s%*s%s)%s\n",
  1052.         type, lenl+lenr?lenl+1:0,
  1053.         left, right, name ? name : "expression");
  1054.     free(left);
  1055.     free(right);
  1056.     free(type);
  1057.     if (name)
  1058.         free(name);
  1059. }
  1060.  
  1061. /* print out a declaration */
  1062. void dodeclare(name, storage, left, right, type)
  1063. char *name, *storage, *left, *right, *type;
  1064. {
  1065.     if (prev == 'v')
  1066.         unsupp("Variable of type void",
  1067.             "variable of type pointer to void");
  1068.  
  1069.     if (*storage == 'r')
  1070.     switch (prev)
  1071.         {
  1072.         case 'f': unsupp("Register function", NullCP); break;
  1073.         case 'A':
  1074.         case 'a': unsupp("Register array", NullCP); break;
  1075.         case 's': unsupp("Register struct/class", NullCP); break;
  1076.         }
  1077.  
  1078.     if (*storage)
  1079.         (void) printf("%s ", storage);
  1080.     (void) printf("%s %s%s%s",
  1081.         type, left,
  1082.     name ? name : (prev == 'f') ? "f" : "var", right);
  1083.     if (MkProgramFlag) {
  1084.         if ((prev == 'f') && (*storage != 'e'))
  1085.             (void) printf(" { }\n");
  1086.         else
  1087.             (void) printf(";\n");
  1088.     } else {
  1089.         (void) printf("\n");
  1090.     }
  1091.     free(storage);
  1092.     free(left);
  1093.     free(right);
  1094.     free(type);
  1095.     if (name)
  1096.         free(name);
  1097. }
  1098.  
  1099. void dodexplain(storage, constvol, type, decl)
  1100. char *storage, *constvol, *type, *decl;
  1101. {
  1102.     if (type && (strcmp(type, "void") == 0))
  1103.     if (prev == 'n')
  1104.         unsupp("Variable of type void",
  1105.            "variable of type pointer to void");
  1106.     else if (prev == 'a')
  1107.         unsupp("array of type void",
  1108.            "array of type pointer to void");
  1109.     else if (prev == 'r')
  1110.         unsupp("reference to type void",
  1111.            "pointer to void");
  1112.  
  1113.     if (*storage == 'r')
  1114.     switch (prev)
  1115.         {
  1116.         case 'f': unsupp("Register function", NullCP); break;
  1117.         case 'A':
  1118.         case 'a': unsupp("Register array", NullCP); break;
  1119.         case 's': unsupp("Register struct/union/enum/class", NullCP); break;
  1120.         }
  1121.  
  1122.     (void) printf("declare %s as ", savedname);
  1123.     if (*storage)
  1124.         (void) printf("%s ", storage);
  1125.     (void) printf("%s", decl);
  1126.     if (*constvol)
  1127.         (void) printf("%s ", constvol);
  1128.     (void) printf("%s\n", type ? type : "int");
  1129. }
  1130.  
  1131. void docexplain(constvol, type, cast, name)
  1132. char *constvol, *type, *cast, *name;
  1133. {
  1134.     if (strcmp(type, "void") == 0)
  1135.     if (prev == 'a')
  1136.         unsupp("array of type void",
  1137.            "array of type pointer to void");
  1138.     else if (prev == 'r')
  1139.         unsupp("reference to type void",
  1140.            "pointer to void");
  1141.     (void) printf("cast %s into %s", name, cast);
  1142.     if (strlen(constvol) > 0)
  1143.         (void) printf("%s ", constvol);
  1144.     (void) printf("%s\n",type);
  1145. }
  1146.  
  1147. /* Do the appropriate things for the "set" command. */
  1148. void doset(opt)
  1149. char *opt;
  1150. {
  1151.     if (strcmp(opt, "create") == 0)
  1152.     { MkProgramFlag = 1; }
  1153.     else if (strcmp(opt, "nocreate") == 0)
  1154.     { MkProgramFlag = 0; }
  1155.     else if (strcmp(opt, "prompt") == 0)
  1156.     { prompting = 1; strcpy(cdecl_prompt, real_prompt); }
  1157.     else if (strcmp(opt, "noprompt") == 0)
  1158.     { prompting = 0; cdecl_prompt[0] = '\0'; }
  1159. #ifndef USE_READLINE
  1160.     /* I cannot seem to figure out what nointeractive was intended to do --
  1161.      * it didn't work well to begin with, and it causes problem with
  1162.      * readline, so I'm removing it, for now.  -i still works.
  1163.      */
  1164.     else if (strcmp(opt, "interactive") == 0)
  1165.     { Interactive = 1; }
  1166.     else if (strcmp(opt, "nointeractive") == 0)
  1167.     { Interactive = 0; OnATty = 0; }
  1168. #endif
  1169.     else if (strcmp(opt, "ritchie") == 0)
  1170.     { CplusplusFlag=0; RitchieFlag=1; PreANSIFlag=0; }
  1171.     else if (strcmp(opt, "preansi") == 0)
  1172.     { CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=1; }
  1173.     else if (strcmp(opt, "ansi") == 0)
  1174.     { CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=0; }
  1175.     else if (strcmp(opt, "cplusplus") == 0)
  1176.     { CplusplusFlag=1; RitchieFlag=0; PreANSIFlag=0; }
  1177. #ifdef dodebug
  1178.     else if (strcmp(opt, "debug") == 0)
  1179.     { DebugFlag = 1; }
  1180.     else if (strcmp(opt, "nodebug") == 0)
  1181.     { DebugFlag = 0; }
  1182. #endif /* dodebug */
  1183. #ifdef doyydebug
  1184.     else if (strcmp(opt, "yydebug") == 0)
  1185.     { yydebug = 1; }
  1186.     else if (strcmp(opt, "noyydebug") == 0)
  1187.     { yydebug = 0; }
  1188. #endif /* doyydebug */
  1189.     else
  1190.     {
  1191.     if ((strcmp(opt, unknown_name) != 0) &&
  1192.         (strcmp(opt, "options") != 0))
  1193.         (void) printf("Unknown set option: '%s'\n", opt);
  1194.  
  1195.     (void) printf("Valid set options (and command line equivalents) are:\n");
  1196.     (void) printf("\toptions\n");
  1197.     (void) printf("\tcreate (-c), nocreate\n");
  1198.     (void) printf("\tprompt, noprompt (-q)\n");
  1199. #ifndef USE_READLINE
  1200.     (void) printf("\tinteractive (-i), nointeractive\n");
  1201. #endif
  1202.     (void) printf("\tritchie (-r), preansi (-p), ansi (-a) or cplusplus (-+)\n");
  1203. #ifdef dodebug
  1204.     (void) printf("\tdebug (-d), nodebug\n");
  1205. #endif /* dodebug */
  1206. #ifdef doyydebug
  1207.     (void) printf("\tyydebug (-D), noyydebug\n");
  1208. #endif /* doyydebug */
  1209.  
  1210.     (void) printf("\nCurrent set values are:\n");
  1211.     (void) printf("\t%screate\n", MkProgramFlag ? "   " : " no");
  1212.     (void) printf("\t%sprompt\n", cdecl_prompt[0] ? "   " : " no");
  1213.     (void) printf("\t%sinteractive\n",
  1214.         (OnATty || Interactive) ? "   " : " no");
  1215.     if (RitchieFlag)
  1216.         (void) printf("\t   ritchie\n");
  1217.     else
  1218.         (void) printf("\t(noritchie)\n");
  1219.     if (PreANSIFlag)
  1220.         (void) printf("\t   preansi\n");
  1221.     else
  1222.         (void) printf("\t(nopreansi)\n");
  1223.     if (!RitchieFlag && !PreANSIFlag && !CplusplusFlag)
  1224.         (void) printf("\t   ansi\n");
  1225.     else
  1226.         (void) printf("\t(noansi)\n");
  1227.     if (CplusplusFlag)
  1228.         (void) printf("\t   cplusplus\n");
  1229.     else
  1230.         (void) printf("\t(nocplusplus)\n");
  1231. #ifdef dodebug
  1232.     (void) printf("\t%sdebug\n", DebugFlag ? "   " : " no");
  1233. #endif /* dodebug */
  1234. #ifdef doyydebug
  1235.     (void) printf("\t%syydebug\n", yydebug ? "   " : " no");
  1236. #endif /* doyydebug */
  1237.     }
  1238. }
  1239.  
  1240. void versions()
  1241. {
  1242.     (void) printf("Version:\n\t%s\n\t%s\n\t%s\n",
  1243.     cdeclsccsid, cdgramsccsid, cdlexsccsid);
  1244.     exit(0);
  1245. }
  1246.  
  1247. int main(argc, argv)
  1248. char **argv;
  1249. {
  1250.     int c, ret = 0;
  1251.  
  1252. #ifdef USE_READLINE
  1253.     /* install completion handlers */
  1254.     rl_attempted_completion_function = (CPPFunction *)attempt_completion;
  1255.     rl_completion_entry_function = (Function *)keyword_completion;
  1256. #endif
  1257.  
  1258.     setprogname(argv[0]);
  1259. #ifdef DOS
  1260.     if (strcmp(progname, "cppdecl") == 0)
  1261. #else
  1262.     if (strcmp(progname, "c++decl") == 0)
  1263. #endif /* DOS */
  1264.     CplusplusFlag = 1;
  1265.  
  1266.     prompting = OnATty = isatty(0);
  1267.     while ((c = getopt(argc, argv, "cipqrpa+dDV")) != EOF)
  1268.     switch (c)
  1269.         {
  1270.         case 'c': MkProgramFlag=1; break;
  1271.         case 'i': Interactive=1; doprompt(); break;
  1272.         case 'q': quiet=1; noprompt(); break;
  1273.  
  1274.         /* The following are mutually exclusive. */
  1275.         /* Only the last one set prevails. */
  1276.         case 'r': CplusplusFlag=0; RitchieFlag=1; PreANSIFlag=0; break;
  1277.         case 'p': CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=1; break;
  1278.         case 'a': CplusplusFlag=0; RitchieFlag=0; PreANSIFlag=0; break;
  1279.         case '+': CplusplusFlag=1; RitchieFlag=0; PreANSIFlag=0; break;
  1280.  
  1281. #ifdef dodebug
  1282.         case 'd': DebugFlag=1; break;
  1283. #endif /* dodebug */
  1284. #ifdef doyydebug
  1285.         case 'D': yydebug=1; break;
  1286. #endif /* doyydebug */
  1287.         case 'V': versions(); break;
  1288.         case '?': usage(); break;
  1289.         }
  1290.  
  1291.     /* Set up the prompt. */
  1292.     if (prompting)
  1293.     strcpy(cdecl_prompt, real_prompt);
  1294.     else
  1295.     cdecl_prompt[0] = '\0';
  1296.  
  1297.     /* Run down the list of arguments, parsing each one. */
  1298.  
  1299.     /* Use standard input if no file names or "-" is found. */
  1300.     if (optind == argc)
  1301.     ret += dostdin();
  1302.  
  1303.     /* If called as explain, declare or cast, or first */
  1304.     /* argument is one of those, use the command line */
  1305.     /* as the input. */
  1306.     else if (namedkeyword(argv[optind]))
  1307.     ret += dotmpfile(argc, argv);
  1308.     else
  1309.     ret += dofileargs(argc, argv);
  1310.  
  1311.     exit(ret);
  1312.     /* NOTREACHED */
  1313. }
  1314.